#!/bin/bash
#
# Flexible download/copy component that uses an underlying transport to
# download files from a server
#
# Author:   Dr. Mike Murphy (mmurphy2@coastal.edu)
# Revision: 19 November 2013
#
#   Copyright 2012-2013 Coastal Carolina University
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#


# Transports are tried in order, if available. If no suitable transport is
# found, all available transports are tried one-by-one. The special
# transport @FAIL may be used to prevent falling back to all available
# transports (e.g. one could force curl with PREFERRED_TRANSPORTS="curl:@FAIL")
declare -g PREFERRED_TRANSPORTS="null:cp:curl:wget:scp"


## p_download <url> <destination>
##
## Downloads a file from <url> and saves it to <destination>. Tries the
## preferred transports, in order. If none of the preferred transports are
## available or can handle the <url>, tries every available transport.
##
## Returns 0 upon success, non-zero on failure.
##
function p_download() {
	local user_agent=$(get_context pulley/user_agent)
	local status=0
	local use_transport=
	local transport=
	
	if [ $# -ne 2 ] || [ -z "$1" ] || [ -z "$2" ]; then
		echo "Invalid download URL or missing destination"
		status=2
	fi
	
	[ -z "${user_agent}" ] && user_agent="Pulley/${PULLEY_VERSION}"
	
	if ((status == 0)); then
		# Try the preferred transports first...
		status=42
		for transport in $(split_string ':' "${PREFERRED_TRANSPORTS}"); do
			if [[ "${transport}" == "@FAIL" ]]; then
				# Search no further: configuration says to stop here
				echo "Not trying all transports, per configuration (@FAIL)"
				status=31
			elif lazy_load p_transport_${transport}_can_download "transports" \
								"${transport}.transport"; then
				p_transport_${transport}_can_download "$1"
				if [[ $? -eq 0 ]]; then
					use_transport="${transport}"
					status=0
					break
				fi
			else
				echo "Warning: preferred transport ${transport} is not available"
			fi
		done
		
		# If none of the preferred transports can handle the URL, try every
		# available transport
		if ((status == 42)); then
			for entry in $(ls "${P_LIBDIR}/transports"/*.transport); do
				transport=$(basename "${entry}" | sed s/\.transport$//)
				if lazy_load p_transport_${transport}_can_download "${transport}" \
								"transports" "${transport}.transport"; then
					p_transport_${transport}_can_download "$1"
					if [[ $? -eq 0 ]]; then
						use_transport="${transport}"
						status=0
						break
					fi
				fi
			done
		fi
		
		# If we have a valid transport, proceed with the download. Otherwise,
		# nobody can handle this particular URL.
		if [[ -n "${use_transport}" ]]; then
			p_transport_${use_transport}_download "$1" "$2" "${user_agent}"
			status=$?
		else
			echo "No suitable transport found for URL: $1"
			status=31
		fi
	else
		echo "Permission denied or directory not found: $2"
	fi
	
	return ${status}
}


## p_download_temp <url>
##
## Downloads a file from <url> and saves it to a temporary file created
## with s_mktempfile (for later garbage collection). If <url> refers to a
## locally mounted path, copies the file into the temporary file.
##
## Returns 0 on success, non-zero otherwise.
##
function p_download_temp() {
	local tmpname=$(s_mktempfile)
	echo "${tmpname}"
	p_download "$1" "${tmpname}"
	return $?
}


## p_download_cache <url> <section>
##
## Downloads a file from <url> and saves it in the specified <section>
## (subdirectory) of the Pulley cache directory. If <url> refers to a
## locally mounted path, copies the file into the cache. Returns 0 on
## success, non-zero otherwise.
##
function p_download_cache() {
	local status=0
	local filename=$(basename "$1")
	
	mkdir -p "${P_CACHEDIR}/$2"
	status=$?
	
	if ((status == 0)); then
		p_download "$1" "${P_CACHEDIR}/$2/${filename}"
		status=$?
	fi
	
	return ${status}
}


## urlencode <string>
##
## Encodes <string> in a URL-safe format. Returns 0.
##
function urlencode() {
	local raw="$1"
	local length=${#raw}
	local result=""
	local ch=""
	local enc=""
	
	for ((idx=0; idx<length; idx++)); do
		ch=${raw:$idx:1}
		
		if [[ "${ch}" =~ [a-zA-Z0-9~_-] ]]; then
			enc="${ch}"
		else
			enc=$(printf '%%%02x' "'${ch}")
		fi
		
		result+="${enc}"
	done
	
	echo "${result}"
	return 0
}
